package util;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class DateUtils {
    
    /**
     * To omit non-intuitive errors with local machine time zone it is imperative to use UTC time zone if other is not directly set while working with string or
     * number representation of date and time.
     * 
     * @see DateUtilities#newCalendar()
     * @see DateUtilities#newDateFormat(String pattern)
     * @author Mykhaylo Adamovych
     */
    public static final TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");
    
    /**
     * Rounds date to specific calendar field in terms of specified time zone.
     * 
     * @param date
     *            the date to floor
     * @param field
     *            the calendar field
     * @param timeZone
     *            the time zone to use
     * @return rounded date
     * @author Mykhaylo Adamovych
     */
    public static Date floor(final Date date, final int field, final TimeZone timeZone) {
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.setTimeZone(timeZone);
        switch (field) {
        case Calendar.DATE: // or Calendar.DAY_OF_MONTH
            calendar.set(Calendar.HOUR_OF_DAY, 0);
        case Calendar.HOUR_OF_DAY:
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            break;
        default:
            throw new RuntimeException(String.format("Unsupported calendar field %n", field));
        }
        return calendar.getTime();
    }
    
    /**
     * Creates and initializes calendar to start work with.
     * 
     * @see DateUtilities#timeZoneUTC
     * @author Mykhaylo Adamovych
     */
    public static Calendar newCalendar() {
        final Calendar result = Calendar.getInstance();
        result.setTimeZone(timeZoneUTC);
        result.setLenient(false);
        result.setTimeInMillis(0);
        return result;
    }
    
    /**
     * Creates and initializes SimpleDateFormat to start work with.
     * 
     * @param pattern
     *            the pattern describing the date and time format
     * @see DateUtilities#timeZoneUTC
     * @see <a href="http://www.codefutures.com/weblog/andygrove/2007/10/simpledateformat-and-thread-safety.html">SimpleDateFormat and Thread Safety</a><br>
     *      <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4146524">Thread safety issue with SimpleDateFormat is known for java community since
     *      quite a long time...</a>
     * @author Mykhaylo Adamovych
     */
    public static ThreadLocal<DateFormat> newDateFormat(final String pattern) {
        final DateFormat dateFormat = new SimpleDateFormat(pattern);
        dateFormat.setTimeZone(timeZoneUTC);
        dateFormat.setLenient(false);
        return new ThreadLocal<DateFormat>() {
            @Override
            protected DateFormat initialValue() {
                return (DateFormat) dateFormat.clone();
            }
        };
    }
    
    /**
     * @see Calendar#add(int, int)
     * @param date
     *            date to change
     * @param calendarField
     *            the calendar field
     * @param amount
     *            the amount of date or time to be added to the field
     * @author Mykhaylo Adamovych
     */
    public static Date add(final Date date, final int calendarField, final int amount) {
        final Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(calendarField, amount);
        return calendar.getTime();
    }
    
}
